package com.gr.staffpm.tasks.dao;

import java.util.Date;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.springframework.stereotype.Repository;

import com.gr.staffpm.dao.HibernateDao;
import com.gr.staffpm.datatypes.Project;
import com.gr.staffpm.datatypes.Task;
import com.gr.staffpm.datatypes.TaskComment;
import com.gr.staffpm.datatypes.TaskImportance;
import com.gr.staffpm.datatypes.TaskStatus;
import com.gr.staffpm.datatypes.User;

/**
 * @author Graham Rhodes 15 Jan 2011 19:23:20
 */
@Repository("taskDAO")
@SuppressWarnings("unchecked")
public class HibernateTaskDAO extends HibernateDao implements TaskDAO {

    private static final long serialVersionUID = 1L;

    @Override
    public TaskComment getComment(int id) {
        return (TaskComment) getSession().get(TaskComment.class, id);
    }

    @Override
    public List<TaskComment> getAllComments(Task task, String property, boolean ascending) {
        Criteria crit = getSession().createCriteria(TaskComment.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq(TaskComment.TASK, task));
        Order order;

        if (property == null) {
            if (ascending)
                order = Order.asc(TaskComment.CREATED);
            else
                order = Order.desc(Task.CREATED);
        } else {
            if (ascending)
                order = Order.asc(property);
            else
                order = Order.desc(property);
        }

        crit.addOrder(order);
        return crit.list();
    }

    @Override
    public void addComment(TaskComment comment) {
        getSession().save(comment);
        getSession().flush();
    }

    @Override
    public Task getTask(int id) {
        return (Task) getSession().get(Task.class, id);
    }

    public List<Task> getAllTasks() {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return crit.list();
    }

    @Override
    public void updateTask(Task task) {
        getSession().saveOrUpdate(task);
    }

    @Override
    public List<TaskStatus> getAllStatuses() {
        Criteria crit = getSession().createCriteria(TaskStatus.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return crit.list();
    }

    @Override
    public List<TaskImportance> getAllImportances() {
        Criteria crit = getSession().createCriteria(TaskImportance.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        return crit.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getInProgressStatus()
     */
    @Override
    public TaskStatus getInProgressStatus() {
        Criteria crit = getSession().createCriteria(TaskStatus.class);
        crit.add(Restrictions.eq(TaskStatus.VALUE, "In Progress"));
        return (TaskStatus) crit.uniqueResult();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getOpenStatus()
     */
    @Override
    public TaskStatus getOpenStatus() {
        Criteria crit = getSession().createCriteria(TaskStatus.class);
        crit.add(Restrictions.eq(TaskStatus.VALUE, "Open"));
        return (TaskStatus) crit.uniqueResult();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getClosedStatus()
     */
    @Override
    public TaskStatus getClosedStatus() {
        Criteria crit = getSession().createCriteria(TaskStatus.class);
        crit.add(Restrictions.eq(TaskStatus.VALUE, "Closed"));
        return (TaskStatus) crit.uniqueResult();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getTasksBetween(java.util.Date, java.util.Date)
     */
    @Override
    public List<Task> getTasksDueBetween(Date startDate, Date endDate) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.between(Task.DUE_DATE, startDate, endDate));
        return crit.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getTasksDueOn(java.util.Date)
     */
    @Override
    public List<Task> getTasksDueOn(Date date) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq(Task.DUE_DATE, date));
        return crit.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getUserTasks(com.gr.staffpm.datatypes.User)
     */
    @Override
    public List<Task> getUserTasks(User user) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq(Task.ASSIGNEE, user));
        return crit.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#findTask(java.lang.String)
     */
    @Override
    public List<Task> findTask(String searchQuery) {

        FullTextSession fullTextSession = Search.getFullTextSession(getSession());

        QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Task.class).get();
        org.apache.lucene.search.Query query = qb.keyword().onFields(Task.NAME, Task.SUMMARY, Task.DESCRIPTION).matching(searchQuery).createQuery();

        // wrap Lucene query in a org.hibernate.Query
        org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery(query, Task.class);

        return hibQuery.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#createIndex()
     */
    @Override
    public void createIndex() {
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
        List<Task> tasks = getAllTasks();
        for (Task task : tasks)
            fullTextSession.index(task);
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getUnresolvedUserTasksBetweenDates(com.gr.staffpm.datatypes.User, java.util.Date, java.util.Date)
     */
    @Override
    public List<Task> getUnresolvedUserTasksBetweenDates(User user, Date start, Date end) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq(Task.ASSIGNEE, user));
        crit.add(Restrictions.eq(Task.RESOLVED, false));
        crit.add(Restrictions.between(Task.DUE_DATE, start, end));
        return crit.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getOpenUserTasks(com.gr.staffpm.datatypes.User, java.lang.String, boolean)
     */
    @Override
    public List<Task> getOpenUserTasks(User user, String property, boolean ascending) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq(Task.ASSIGNEE, user));
        crit.add(Restrictions.or(Restrictions.eq(Task.STATUS, getOpenStatus()), Restrictions.eq(Task.STATUS, getInProgressStatus())));
        Order order;

        if (property == null) {
            if (ascending)
                order = Order.asc(Task.NAME);
            else
                order = Order.desc(Task.NAME);
        } else {
            if (ascending)
                order = Order.asc(property);
            else
                order = Order.desc(property);
        }

        crit.addOrder(order);
        return crit.list();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#countOpenUserTasks(com.gr.staffpm.datatypes.User)
     */
    @Override
    public int countOpenUserTasks(User user) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.add(Restrictions.eq(Task.ASSIGNEE, user));
        crit.add(Restrictions.or(Restrictions.eq(Task.STATUS, getOpenStatus()), Restrictions.eq(Task.STATUS, getInProgressStatus())));
        crit.setProjection(Projections.rowCount());
        return ((Long) crit.uniqueResult()).intValue();
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#createTask(com.gr.staffpm.datatypes.Task)
     */
    @Override
    public Task createTask(Task newTask) {
        int id = (Integer) getSession().save(newTask);
        newTask.setTaskId(id);
        return newTask;
    }

    /* (non-Javadoc)
     * @see com.gr.staffpm.tasks.dao.TaskDAO#getUnresolvedProjectTasks(com.gr.staffpm.datatypes.Project)
     */
    @Override
    public List<Task> getUnresolvedProjectTasks(Project project) {
        Criteria crit = getSession().createCriteria(Task.class);
        crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        crit.add(Restrictions.eq(Task.PROJECT, project));
        crit.add(Restrictions.eq(Task.RESOLVED, false));
        return crit.list();
    }

}
